import type { MatcherLocationAsPath } from 'vue-router'
import type { RouteRecordRaw, RouteRecordMatcher, RouteRecordNormalized } from './types'

// 对开发者所传入的路由记录做了一个标准化处理
function normalizeRouteRecord(record: RouteRecordRaw): RouteRecordNormalized {
  return {
    path: record.path,
    meta: record.meta || {},
    beforeEnter: record.beforeEnter,
    name: record.name || '',
    components: {
      default: record.component
    },
    children: record.children || [],
    leaveGuards: [],
    updateGuards: []
  }
}

// 该方法负责根据一个标准化后的路由记录，生成对应的 matcher
function createRouteRecordMatcher(record: RouteRecordNormalized, parent?: RouteRecordMatcher) {
  const matcher: RouteRecordMatcher = {
    path: record.path,
    record,
    parent,
    children: []
  }

  // 查看是否存在父级路由，如果存在，那么需要将当前的 matcher 对象推入到 parent.children 里面
  if (parent) {
    parent.children.push(matcher)
  }

  return matcher
}

export function createRouterMatcher(routes: Readonly<RouteRecordRaw[]>) {
  // 创建一个数组来存储 matcher
  const matchers: RouteRecordMatcher[] = []

  /**
   * 添加路由方法
   * @param route
   * @param parent
   */
  function addRoute(route: RouteRecordRaw, parent?: RouteRecordMatcher) {
    // 1. 首先将 RouteRecordRaw 转为 normalize 类型
    const normalizedRecord = normalizeRouteRecord(route)

    // 2. 查看是否存在父级路由，如果存在，那么 path 这个部分的父亲路由路径也需要拼接进去
    if (parent) {
      normalizedRecord.path = parent.path + normalizedRecord.path
    }

    // 3. 创建对应的 matcher（RouteRecordMatcher）
    const matcher = createRouteRecordMatcher(normalizedRecord, parent)

    // 4. 需要查看 normalizedRecord 是否有 children，如果有说明存在子路由
    // 那么针对子路由我们也需要做相同的处理
    if ('children' in normalizedRecord) {
      const children = normalizedRecord.children
      for (let i = 0; i < children.length; i++) {
        addRoute(children[i], matcher)
      }
    }

    // 5. 将当前的 matcher 推入到 matchers 数组里面进行保存
    matchers.push(matcher)
  }

  // 该方法主要是做信息解析的
  function resolve(location: MatcherLocationAsPath) {
    // 存放匹配上的路由记录
    const matched: RouteRecordNormalized[] = []

    const path = location.path

    // 接下来去 matchers 数组里面寻找和 path 匹配的 matcher
    let matcher = matchers.find((m) => m.path === path)

    // 只要这个 matcher 存在，通过 while 去寻找匹配上的记录
    // 将找到的匹配记录放入到 matched 数组里面
    while (matcher) {
      // 注意，这里是放入到数组的最前面
      // 这样放置的目的是为了整个顺序是从外到内，从父路由到子路由
      matched.unshift(matcher.record)
      matcher = matcher.parent
    }
    return {
      path,
      matched
    }
  }

  // 添加路由
  routes.forEach((route) => addRoute(route))

  // 向外部暴露这两个方法
  return {
    addRoute,
    resolve
  }
}

